package com.kdgc.power.fw.service;

import com.kdgc.power.common.util.DateUtil;
import com.kdgc.power.common.util.MathUtil;
import com.kdgc.power.common.util.StringUtil;
import com.kdgc.power.fw.bean.BaseEntity;
import com.kdgc.power.fw.bean.SystemConfig;
import com.kdgc.power.fw.entity.DbTable;
import org.beetl.sql.clazz.kit.BeanKit;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.SqlId;
import org.beetl.sql.core.page.PageResult;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Stream;


@Service
public class BaseService<T extends BaseEntity> {
    @Resource
    private BeetSqlHelp bsh;
    @Resource
    @Qualifier(SystemConfig.MAIN_SQL_MANAGER)
    private SQLManager sqlManager;
    @Resource
    private DbTableService dbTableService;

    public Class<T> getTClass() {
        return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public String getMaxCode(T entity) {
        String className = entity.getClass().getSimpleName();
        String table = StringUtil.toUnderline(className);
        String codeFieldName = entity.getCodeFieldName();
        if (StringUtil.isNotEmpty(codeFieldName)) {
            String column = StringUtil.toUnderline(codeFieldName);
            Map<String, Object> paras = new HashMap<>();
            paras.put("table", table);
            paras.put("column", column);
            List<Map<String, Object>> maps = bsh.searchBySql("select max(${column}) as max_code from ${table}", paras);
            return StringUtil.toString(maps.get(0).get("maxCode"));
        } else {
            return null;
        }

    }

    /**
     * 自动根据主键判断是新增或修改
     */
    public boolean saveOrUpdate(T entity) {
        boolean isAutoInsert=false;
// 处理主键自增
        String autoFieldName = entity.getAutoFieldName();
//        主键自增且没有主键
        if (StringUtil.isNotEmpty(autoFieldName) && StringUtil.isEmpty(BeanKit.getBeanProperty(entity, autoFieldName))) {
            isAutoInsert=true;
            String tableName = StringUtil.toUnderline(entity.getClass().getSimpleName()).toUpperCase();
            DbTable dbTable = dbTableService.findByTableName(tableName );
            if (StringUtil.isEmpty(dbTable)) {
                DbTable db = new DbTable();
                db.setTableName(tableName);
                db.setTableKey(StringUtil.toUnderline(autoFieldName).toUpperCase());
                db.setMaxCode(1);
                BeanKit.setBeanProperty(entity,1,autoFieldName);
                sqlManager.insert(db);
            }else {
                Integer newCode = dbTable.getMaxCode()+1;
                BeanKit.setBeanProperty(entity,newCode,autoFieldName);
                dbTable.setMaxCode(newCode);
                sqlManager.updateById(dbTable);
            }
        }

        entity.setIsUse("Y");
//        处理新增时Code编码
        String codeName = entity.getCodeFieldName();
        Map<String, Object> codeInfo = entity.getCodeInfo();
        if (StringUtil.isNotEmpty(codeName)) {
            String maxCode = getMaxCode(entity);
            String prefix = StringUtil.toString(codeInfo.get("prefix"));
            int length = StringUtil.toInt(codeInfo.get("length"));
            String today = DateUtil.format(new Date(), "yyyyMMdd");
            if (maxCode.startsWith(prefix + today)) {
                int startLength = prefix.length();
                long longCode = StringUtil.toLong(maxCode.substring(startLength)) + 1;
                BeanKit.setBeanProperty(entity, prefix + longCode, codeName);
            } else {
                BeanKit.setBeanProperty(entity, prefix + today + MathUtil.numberTo0(length - 1) + "1", codeName);
            }
        }
        if(isAutoInsert){
            sqlManager.insert(entity);
            return true;
        }else {
            return sqlManager.upsert(entity);
        }

    }

    public int insert(T entity) {
        return sqlManager.insert(entity.getClass(), entity);
    }

    public int update(T entity) {
        return sqlManager.updateById(entity);
    }

    public T getEntity(Object id) {
        return sqlManager.single(getTClass(), id);
    }

    public Map<String, Object> getMapEntity(Integer id) {
        T entity = sqlManager.single(getTClass(), id);
        if (StringUtil.isNotEmpty(entity)) {
            Class<? extends BaseEntity> clazz = entity.getClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            Map<String, Object> entityMap = new HashMap<>();
            Stream<Field> childStream = Arrays.stream(declaredFields);
            Class<?> superclass = clazz.getSuperclass();
            Stream<Field> parentStream = Arrays.stream(superclass.getDeclaredFields());
            Stream.concat(childStream, parentStream).forEach(a -> {
                if (a.getType() == Date.class) {
                    Date aDate = (Date) BeanKit.getBeanProperty(entity, a.getName());
                    DateTimeFormat annotation = a.getAnnotation(DateTimeFormat.class);
                    String pattern = annotation.pattern();
                    SimpleDateFormat format = new SimpleDateFormat(pattern);
                    entityMap.put(a.getName(), format.format(aDate));
                } else {
                    entityMap.put(a.getName(), BeanKit.getBeanProperty(entity, a.getName()));
                }
            });
            return entityMap;
        } else {
            return null;
        }
    }

    public int deleteById(Object id) {
        return sqlManager.deleteById(getTClass(), id);
    }

    public int removeById(Object id) {
        T entity = getEntity(id);
        entity.setIsUse("N");
        return sqlManager.updateById(entity);
    }


    public PageResult<Map<String, Object>> getPage(String sqlId, Map<String, Object> paras, Integer page, Integer rows) {
        return bsh.getPage(SqlId.of(sqlId), paras, page, rows);
    }


}
